package com.itheima.health.security;

import com.itheima.health.pojo.Permission;
import com.itheima.health.pojo.Role;
import com.itheima.health.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.*;

/** 引入的starter依赖中会自动配置一个过滤器
 springSecurity权限控制实现流程原理：
 认证（UsernamePasswordAuthenticationFilter）
 用户访问tomcat服务器，被第一个过滤器UsernamePasswordAuthenticationFilter拦下
 然后这个过滤器调用认证管理者（AuthenticationManage）此类会调用anthenticate()方法对用户进行认证（验证用户名和密码是否正确）如果正确会到下一个过滤器

 鉴权（filterSecurityInterceptor过滤器）：鉴权过滤器调用访问决定者（AccessDecisionManager）管理器调用decide（）方法 判断用户是否可以访问资源
*/

/**
认证详情：AuthenticationManage（认证管理器）接口的providerManager实现类调用DaoAuthenticationProvider(认证提供者)调用UserserviceDetailsSerivce（接口）获取用户权限和角色信息和密码（加密的） 然后调用PasswordEncoder校验密码是否正确
 PasswordEncoder的实现类DelegatingPasswordEncoding里边Map（{bcrypt} 不同的名词对应不同加密器的实现类） 包含很多加密器供调用
 */
@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {


    //加密
    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    String ciper = bCryptPasswordEncoder.encode("1234");  //单向散列函数+动态盐

    //创建静态数据用于测试   真实的生产环境中要从数据库中查找
    private static Map<String, User> userDb = new HashMap();
    static {
        //bcrypt加密
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        User user1 = new User();
        user1.setUsername("admin");
        user1.setPassword("{noop}1234");
        // 用户权限与角色
        Role role1 = new Role("系统管理员","ROLE_ADMIN");
        role1.getPermissions().add(new Permission("添加权限","add"));
        role1.getPermissions().add(new Permission("删除权限","delete"));
        role1.getPermissions().add(new Permission("更新权限","update"));
        role1.getPermissions().add(new Permission("查询权限","find"));
        user1.getRoles().add(role1);
        userDb.put(user1.getUsername(),user1);

        User userZhangSan = new User();
        userZhangSan.setUsername("zhangsan");
        userZhangSan.setPassword("{bcrypt}"+bCryptPasswordEncoder.encode("1234"));  //用bcrypt加密密码
        Role role2 = new Role("数据分析员","ROLE_READER");
        role2.getPermissions().add(new Permission("查询权限","find"));
        userZhangSan.getRoles().add(role2);
        userDb.put(userZhangSan.getUsername(),userZhangSan);

        User userLisi = new User();
        userLisi.setUsername("lisi");
        userLisi.setPassword("{noop}1234");
        Role role3 = new Role("运营管理员","ROLE_OMS");
        role3.getPermissions().add(new Permission("添加权限","add"));
        role3.getPermissions().add(new Permission("更新权限","update"));
        userLisi.getRoles().add(role3);
        userDb.put(userLisi.getUsername(),userLisi);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //数据库中的pojo类  查询出信息 然后封装到UserDetails中
        User user = userDb.get(username);

        //用户不存在  必须抛出UsernameNotFoundException异常
        if(null==user){
            throw new UsernameNotFoundException("用户不存在" + username);
        }
        List<GrantedAuthority> authorities = new ArrayList<>();
        //构造authorities
        Set<Role> roles = user.getRoles();   //角色中包含权限
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getKeyword()));
            for (Permission permission : role.getPermissions()) {
                authorities.add(new SimpleGrantedAuthority(permission.getKeyword()));
            }
        }
        log.info("[获取用户认证权限权信息]userName:{},authorities:{}", user.getUsername(), authorities);

        //用户存在  返回UserDetails
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
    }
}
